🎖️GitЯра🎖️
Node / meshtastic / Meshtastic-Android / files / core / service / src / androidMain / kotlin / org / meshtastic / core / service / ForegroundStartPolicy.kt
Displaying Raw • Download
core/service/src/androidMain/kotlin/org/meshtastic/core/service/ForegroundStartPolicy.kt bd2863243bab6eb213401d949839a2bc74dde7e2 (bd286324) Text, 5.70 KB
T8b949e/*
* Copyright (c) 2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
Tff7b72package T7ee787org.meshtastic.core.service
Tff7b72import T7ee787android.content.pm.ServiceInfo
Tff7b72import T7ee787android.os.Build
T8b949e/**
* Decides whether starting [MeshService] as a foreground service is legal, and which foreground service types it may
* claim.
*
* Deliberately pure: every platform fact arrives as a parameter so the rules can be unit tested without a device. The
* Android probes that supply those facts live in `ForegroundStartContext.kt`.
*
* Two independent platform gates are modelled here.
* 1. **Background-start restriction (Android 12+).** `startForegroundService()` throws
* `ForegroundServiceStartNotAllowedException` unless the caller is in an allowlisted context. Covered by
* [isForegroundStartAllowed].
* 2. **While-in-use type restriction (Android 14+).** A `location` foreground service cannot be *started* from the
* background without `ACCESS_BACKGROUND_LOCATION`, which this app does not declare; attempting it throws
* `SecurityException`. `connectedDevice` carries no such restriction. Covered by [foregroundServiceType].
*/
Tff7b72object T56d364ForegroundStartPolicy Tb4b4b4{
T8b949e/** Android 12 (API 31) introduced the background foreground-service start restriction. */
Tff7b72private Tff7b72const Tff7b72val Te6edf3SDK_BACKGROUND_START_RESTRICTED Tff7b72= Te6edf3BuildTb4b4b4.Te6edf3VERSION_CODESTb4b4b4.Te6edf3S
T8b949e/** Android 10 (API 29) introduced foreground service types. */
Tff7b72private Tff7b72const Tff7b72val Te6edf3SDK_SERVICE_TYPES Tff7b72= Te6edf3BuildTb4b4b4.Te6edf3VERSION_CODESTb4b4b4.Te6edf3Q
T8b949e/** Android 14 (API 34) began enforcing while-in-use restrictions on the `location` type at start time. */
Tff7b72private Tff7b72const Tff7b72val Te6edf3SDK_WHILE_IN_USE_ENFORCED Tff7b72= Te6edf3BuildTb4b4b4.Te6edf3VERSION_CODESTb4b4b4.Te6edf3UPSIDE_DOWN_CAKE
T8b949e/**
* Returns true when `Context.startForegroundService()` is expected to succeed for [trigger].
*
* Returning false is not a failure to recover from — it means the platform would reject the start, so the app must
* not make the call at all. The service is picked up by the next legitimate trigger, which in practice is the user
* opening the app ([ServiceStartTrigger.UserInterface]).
*
* @param appInForeground whether this process currently holds a user-visible component.
* @param sdkInt the running platform API level.
*/
Tff7b72fun Td2a8ffisForegroundStartAllowedTb4b4b4(
Te6edf3triggerTb4b4b4: Te6edf3ServiceStartTriggerTb4b4b4,
Te6edf3appInForegroundTb4b4b4: Tffa657BooleanTb4b4b4,
Te6edf3sdkIntTb4b4b4: Tffa657Int Tff7b72= Te6edf3BuildTb4b4b4.Te6edf3VERSIONTb4b4b4.Te6edf3SDK_INTTb4b4b4,
Tb4b4b4)Tb4b4b4: Tffa657Boolean Tff7b72= Tff7b72when Tb4b4b4{
T8b949e// Before Android 12 there is no background-start restriction at all.
Te6edf3sdkInt Tff7b72< Te6edf3SDK_BACKGROUND_START_RESTRICTED Tff7b72-Tff7b72> Tff7b72true
T8b949e// An activity moving to started is the canonical "transitions from a user-visible state" exemption, and
T8b949e// BOOT_COMPLETED / MY_PACKAGE_REPLACED are named exemptions. Neither depends on current process importance:
T8b949e// at boot the process is not yet user-visible, which is precisely what the exemption exists for.
Te6edf3trigger Tff7b72=Tff7b72= Te6edf3ServiceStartTriggerTb4b4b4.Te6edf3UserInterface Tff7b72-Tff7b72> Tff7b72true
Te6edf3trigger Tff7b72=Tff7b72= Te6edf3ServiceStartTriggerTb4b4b4.Te6edf3BootCompleted Tff7b72-Tff7b72> Tff7b72true
T8b949e// No exemption of its own — legal only while the app is genuinely still in the foreground.
Tff7b72else Tff7b72-Tff7b72> Te6edf3appInForeground
Tb4b4b4}
T8b949e/**
* Returns the foreground service type bitmask to pass to `startForeground()`.
*
* `connectedDevice` is always claimed: it is the type that actually describes the radio link, it is not subject to
* while-in-use restrictions, and its prerequisite (`BLUETOOTH_CONNECT`) is declared. `location` is additive and is
* claimed only when it can be held legally, because requesting it in a state the platform forbids throws
* `SecurityException` and takes the whole start down with it.
*
* @param hasLocationPermission whether `ACCESS_FINE_LOCATION` is granted.
* @param appInForeground whether this process currently holds a user-visible component.
* @param sdkInt the running platform API level.
*/
Tff7b72fun Td2a8ffforegroundServiceTypeTb4b4b4(
Te6edf3hasLocationPermissionTb4b4b4: Tffa657BooleanTb4b4b4,
Te6edf3appInForegroundTb4b4b4: Tffa657BooleanTb4b4b4,
Te6edf3sdkIntTb4b4b4: Tffa657Int Tff7b72= Te6edf3BuildTb4b4b4.Te6edf3VERSIONTb4b4b4.Te6edf3SDK_INTTb4b4b4,
Tb4b4b4)Tb4b4b4: Tffa657Int Tb4b4b4{
Tff7b72if Tb4b4b4(Te6edf3sdkInt Tff7b72< Te6edf3SDK_SERVICE_TYPESTb4b4b4) Tff7b72return T79c0ff0
Tff7b72val Te6edf3canClaimLocation Tff7b72=
Te6edf3hasLocationPermission Tff7b72&Tff7b72&
T8b949e// From Android 14 the location type is while-in-use restricted at start time. The app does not declare
T8b949e// ACCESS_BACKGROUND_LOCATION, so a background start can never hold it.
Tb4b4b4(Te6edf3sdkInt Tff7b72< Te6edf3SDK_WHILE_IN_USE_ENFORCED Tff7b72|Tff7b72| Te6edf3appInForegroundTb4b4b4)
Tff7b72return Tff7b72if Tb4b4b4(Te6edf3canClaimLocationTb4b4b4) Tb4b4b4{
Te6edf3ServiceInfoTb4b4b4.Te6edf3FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE Te6edf3or Te6edf3ServiceInfoTb4b4b4.Te6edf3FOREGROUND_SERVICE_TYPE_LOCATION
Tb4b4b4} Tff7b72else Tb4b4b4{
Te6edf3ServiceInfoTb4b4b4.Te6edf3FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE
Tb4b4b4}
Tb4b4b4}
T8b949e/**
* The fallback type used when [foregroundServiceType] is still rejected at runtime. Always safe to claim when the
* service may run at all.
*/
Tff7b72fun Td2a8fffallbackForegroundServiceTypeTb4b4b4(Te6edf3sdkIntTb4b4b4: Tffa657Int Tff7b72= Te6edf3BuildTb4b4b4.Te6edf3VERSIONTb4b4b4.Te6edf3SDK_INTTb4b4b4)Tb4b4b4: Tffa657Int Tff7b72=
Tff7b72if Tb4b4b4(Te6edf3sdkInt Tff7b72< Te6edf3SDK_SERVICE_TYPESTb4b4b4) T79c0ff0 Tff7b72else Te6edf3ServiceInfoTb4b4b4.Te6edf3FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE
Tb4b4b4}
Served by rngit 1.5.0 - Generated in 0.07s